using Cysharp.Threading.Tasks;
using IQIGame.Onigao.Framework;
using System.Collections.Generic;

namespace IQIGame.Onigao.GamePlay
{
    public class GameplayAreaManager : Singleton<GameplayAreaManager>
    {
        private Dictionary<GameplayAreaType, IGameplayArea> _areaMap = new();
        private IGameplayArea _curArea;
        private bool _isSwitchingGameplayArea;
        private AutoResetUniTaskCompletionSource _levelSwitchTaskCompletionSource;

        /// <summary>
        /// 是否正在切换GameplayArea
        /// </summary>
        public bool isSwitchingGameplayArea => _isSwitchingGameplayArea;
        /// <summary>
        /// 当前所在区域
        /// </summary>
        public GameplayAreaType curAreaType { get; private set; }
        public IGameplayArea curArea => _curArea;
        public IGameplaySceneArea curSceneArea => _curArea as IGameplaySceneArea;

        private GameplayAreaManager()
        {
            Register();
        }

        public void Register()
        {
            _areaMap.Add(GameplayAreaType.MainCity, MainCityModule.Instance);
            _areaMap.Add(GameplayAreaType.Level, LevelPlayModule.Instance);
        }

        public void Switch(GameplayAreaType areaType, object userData = null)
        {
            SwitchAwait(areaType, userData).Forget();
        }

        public async UniTask SwitchAwait(GameplayAreaType areaType, object userData = null)
        {
            //注意该方法不要判断if (areaType == curAreaType) return;因为要支持退出关卡后马上进入另一个关卡，中间是不能加载主城的
            if (_isSwitchingGameplayArea)
            {
                LogGame.LogError("正在切换GameplayArea");
                return;
            }
            MsgDispatcher.Broadcast(MsgEventType.StartSwitchGameplayArea);
            _isSwitchingGameplayArea = true;
            LeaveCurArea();

            if (!_areaMap.TryGetValue(areaType, out var area))
            {
                LogGame.LogError($"类型 {areaType} 未注册。");
                return;
            }

            curAreaType = areaType;

            _curArea = area;
            await _curArea.OnEnter(userData);
            _isSwitchingGameplayArea = false;
            //最后尝试完成切换关卡的任务，该逻辑只针对客户端发起的进出关卡请求，虽然不是公共逻辑，但为了能方便的进行异步调用，所以这里来处理
            //如果不是进出关卡，taskSource是空，该方法不会执行任何逻辑，否则，完成该任务代表发起进出关卡请求后，场景已经切换完毕
            TryCompleteLevelSwitchTask();
        }

        private void LeaveCurArea()
        {
            _curArea?.OnLeave();
            _curArea = null;
            curAreaType = GameplayAreaType.None;
        }

        #region 特别处理进出关卡请求导致的GameplayArea切换任务
        public UniTask NewLevelSwitchTask()
        {
            if (_levelSwitchTaskCompletionSource != null)
            {
                _levelSwitchTaskCompletionSource.TrySetCanceled();
            }
            _levelSwitchTaskCompletionSource = AutoResetUniTaskCompletionSource.Create();
            return _levelSwitchTaskCompletionSource.Task;
        }

        public void CancelLevelSwitchTask()
        {
            if (_levelSwitchTaskCompletionSource != null)
            {
                // 注意！这里要先置空，再调用，TrySetResult会先调用await之后的代码，如果await之后有给TaskSource赋新值，然后这里再置空，那么_completeActTaskSource就永远为null了
                var ts = _levelSwitchTaskCompletionSource;
                _levelSwitchTaskCompletionSource = null;
                ts.TrySetCanceled();
            }
        }

        private void TryCompleteLevelSwitchTask()
        {
            if (_levelSwitchTaskCompletionSource != null)
            {
                // 注意！这里要先置空，再调用，TrySetResult会先调用await之后的代码，如果await之后有给TaskSource赋新值，然后这里再置空，那么_completeActTaskSource就永远为null了
                var ts = _levelSwitchTaskCompletionSource;
                _levelSwitchTaskCompletionSource = null;
                ts.TrySetResult();
            }
        }
        #endregion 特别处理进出关卡请求导致的GameplayArea切换任务

        public void QuitGame()
        {
            LeaveCurArea();
        }
    }
}
